/*
*	Copyright (C) 2014  Julio Montes, Mario Carrillo
*
*	Julio Montes e-mail: imc.coder@gmail.com
*
*	Mario Carrillo e-mail: mario_90ago@live.com.mx
*
*	BSD 3-Clause License
*/

#include "sdk/include/lexer.h"

#include "sdk/include/util.h"

#include <algorithm>
#include <utility>
#include <wx/tokenzr.h>

namespace sdk
{
    bool sortKeywords( const wxString &i, const wxString &j )
    {
        return i.BeforeLast( wxT( '?' ) ).CmpNoCase( j.BeforeLast( wxT( '?' ) ) ) < 0;
    }

    bool matchKeyword( const wxString &i, const wxString &j )
    {
        return i.BeforeLast( wxT( '?' ) ).StartsWith( j.BeforeLast( wxT( '?' ) ) );
    }

    bool matchKeywordNoCase( const wxString &i, const wxString &j )
    {
        return i.BeforeLast( wxT( '?' ) ).Lower().StartsWith( j.BeforeLast( wxT( '?' ) ).Lower() );
    }




    Lexer::Lexer()
        : m_modified( false )
    {

    }

    Lexer::~Lexer()
    {

    }

    bool Lexer::LoadFile( const wxString &filename )
    {
        m_filename = filename;
        const bool result = m_fileLexer.LoadFile( filename );
        Reload();
        return result;
    }

    wxString Lexer::GetName() const
    {
        return m_fileLexer.ReadString( wxT( "/lexer" ), wxT( "name" ) );
    }

    wxString Lexer::GetFileMask() const
    {
        return m_fileLexer.ReadString( wxT( "/lexer" ), wxT( "filemask" ) );
    }

    wxString Lexer::GetSupportedFiles() const
    {
        return m_fileLexer.ReadString( wxT( "/lexer" ), wxT( "supportedFiles" ) );
    }

    int Lexer::GetIndex() const
    {
        return m_fileLexer.ReadInt( wxT( "/lexer" ), wxT( "index" ) );
    }

    bool Lexer::HasFileMask( const wxString &fileMask ) const
    {
        wxStringTokenizer st( GetFileMask(), wxT( ", " ), wxTOKEN_STRTOK );
        while( st.HasMoreTokens() )
        {
            if( !st.GetNextToken().CmpNoCase( fileMask ) )
                return true;
        }

        return false;
    }

    bool Lexer::HasSupportedFile( const wxString &fileName ) const
    {
        wxStringTokenizer st( GetSupportedFiles(), wxT( ", " ), wxTOKEN_STRTOK );
        while( st.HasMoreTokens() )
        {
            if( !st.GetNextToken().Cmp( fileName ) )
                return true;
        }

        return false;
    }

    size_t Lexer::GetStyles( sdk::vectorStyles *vecStyles ) const
    {
        std::vector<Element> vectorElements;
        m_fileLexer.ReadElements( wxT( "/lexer/styles/set" ), &vectorElements );

        for( std::vector<Element>::const_iterator it = vectorElements.begin(); it != vectorElements.end(); ++it )
        {
            sdk::styleLexer l_style = { wxT( "" ), 0, wxNullColour, wxNullColour, false, false, false };
            const sdk::mapAttributes &mapAttrs = it->m_mapAttributes;

            for( sdk::mapAttributes::const_iterator itAttrs = mapAttrs.begin(); itAttrs != mapAttrs.end(); ++itAttrs )
            {
                const wxString &l_name = itAttrs->first;

                if( !l_name.CmpNoCase( wxT( "name" ) ) )
                    l_style.name = itAttrs->second;

                else if( !l_name.CmpNoCase( wxT( "index" ) ) )
                    itAttrs->second.ToLong( (long *) &l_style.index );

                else if( !l_name.CmpNoCase( wxT( "fg" ) ) )
                    l_style.foreground = wxColor( itAttrs->second );

                else if( !l_name.CmpNoCase( wxT( "bg" ) ) )
                    l_style.background = wxColor( itAttrs->second );

                else if( !l_name.CmpNoCase( wxT( "bold" ) ) )
                    l_style.bold = itAttrs->second != wxT( "0" ) ? true : false;

                else if( !l_name.CmpNoCase( wxT( "italic" ) ) )
                    l_style.italic = itAttrs->second != wxT( "0" ) ? true : false;

                else if( !l_name.CmpNoCase( wxT( "under" ) ) )
                    l_style.underline = itAttrs->second != wxT( "0" ) ? true : false;

            }

            vecStyles->push_back( l_style );

        }

        return vecStyles->size();
    }


    size_t Lexer::GetKeywords( sdk::vectorWords *vecWords ) const
    {
        std::vector<Element> vectorElements;
        m_fileLexer.ReadElements( wxT( "/lexer/words/set" ), &vectorElements );

        for( std::vector<Element>::const_iterator it = vectorElements.begin(); it != vectorElements.end(); ++it )
        {
            sdk::keywordsLexer l_keywords = { wxT( "" ), 0, wxT( "" ) };
            const sdk::mapAttributes &mapAttrs = it->m_mapAttributes;

            for( sdk::mapAttributes::const_iterator itAttrs = mapAttrs.begin(); itAttrs != mapAttrs.end(); ++itAttrs )
            {
                const wxString &l_name = itAttrs->first;

                if( !l_name.CmpNoCase( wxT( "name" ) ) )
                    l_keywords.name = itAttrs->second;

                else if( !l_name.CmpNoCase( wxT( "index" ) ) )
                    itAttrs->second.ToLong( (long *) &l_keywords.index );

                else if( !l_name.CmpNoCase( wxT( "value" ) ) )
                    l_keywords.keywords = itAttrs->second;
            }

            vecWords->push_back( l_keywords );

        }

        return vecWords->size();
    }

    size_t Lexer::GetImages( sdk::vectorImages *vecImages ) const
    {
        std::vector<Element> vectorElements;
        m_fileLexer.ReadElements( wxT( "/lexer/images/set" ), &vectorElements );

        for( std::vector<Element>::const_iterator it = vectorElements.begin(); it != vectorElements.end(); ++it )
        {
            sdk::imageLexer l_image = { 0, wxT( "" ) };
            const sdk::mapAttributes &mapAttrs = it->m_mapAttributes;

            for( sdk::mapAttributes::const_iterator itAttrs = mapAttrs.begin(); itAttrs != mapAttrs.end(); ++itAttrs )
            {
                const wxString &l_name = itAttrs->first;

                if( !l_name.CmpNoCase( wxT( "index" ) ) )
                    itAttrs->second.ToLong( (long *) &l_image.index );

                else if( !l_name.CmpNoCase( wxT( "value" ) ) )
                    l_image.image = itAttrs->second;
            }

            vecImages->push_back( l_image );
        }

        return vecImages->size();
    }

    wxString Lexer::GetWordsWith( const wxString &word, bool noCase ) const
    {
        std::pair<std::vector<wxString>::const_iterator, std::vector<wxString>::const_iterator> bounds =
        sdk::equalRange( m_vectorWords.begin(), m_vectorWords.end(), word + wxT( "?" ), sortKeywords, noCase ? matchKeywordNoCase : matchKeyword );

        wxString l_keywords;
        while( bounds.first != bounds.second )
        {
            l_keywords += *bounds.first + wxT( " " );
            ++bounds.first;
        }

        return l_keywords;
    }

    void Lexer::Reload()
    {
        sdk::vectorWords l_vectorWords;
        GetKeywords( &l_vectorWords );
        m_vectorWords.clear();

        for( size_t i=0; i<l_vectorWords.size(); ++i )
        {
            wxStringTokenizer st( l_vectorWords[i].keywords, wxT( " \t\n\r" ), wxTOKEN_STRTOK );
            while( st.HasMoreTokens() )
            {
                m_vectorWords.push_back( wxString::Format( wxT( "%ls?%d" ), st.GetNextToken().c_str(), l_vectorWords[i].index ) );
            }
        }

        std::sort( m_vectorWords.begin(), m_vectorWords.end(), sortKeywords );
    }
}
